<?php

class APhpBbIntegration
{

    protected static $m_bbPrefix = 'phpbb_';

    /**
    *   Creates user in phpBB
    *
    *   @param integer $userId ID of user to create
    *   @param string $username username to login
    *   @param string $pass password to login
    *   @param string $email
    *   @param string $userLng 2 letter code of interface language (default is 'en')
    *   @param string $dateF format of displayed datetime (default is 'd M Y H:i')
    *   @return boolean was user created
    */
    public static function createAccount($userId, $username, $pass, $email, $userLng = 'en', $dateF = 'd M Y H:i')
    {
        $isOk = false;

        $usernameClean = strtolower($username);
        // Email hash (CRC32) with the length
        $emailHash = sprintf('%u', crc32(strtolower($email))) . strlen($email);
        debugvar('Hash: ' . $emailHash);
        
        $sql = sprintf(
            'INSERT INTO `%susers` (
                user_id, username, username_clean, user_password, user_type, group_id, user_email, user_email_hash, user_lang, user_dateformat
                , user_regdate, user_passchg, user_lastvisit, user_permissions, user_pass_convert, user_style
                , user_rank, user_full_folder, user_topic_sortby_type, user_topic_sortby_dir, user_post_sortby_type
                , user_post_sortby_dir, user_notify_pm, user_allow_pm, user_allow_viewonline, user_allow_massemail
                , user_options, user_avatar, user_avatar_type, user_avatar_width, user_avatar_height, user_sig_bbcode_uid) 
                            VALUES(
                                %d, "%s", "%s", "%s", %d, %d, "%s", "%s", "%s", "%s"
                                %d, %d, %d
                            )'
            , self::$m_bbPrefix
            , $userId
            , $username
            , $usernameClean
            , self::phpBbHash($pass)
            , 0
            , 16805
            , $email
            , $emailHash
            , $userLng
            , $dateF
            , time()
            , time()
            , time()
        );

        debugvar($sql);

        return $isOk;
    }

    public static function getUserMaxId()
    {
        $res = self::executeSql('SELECT `user_id` FROM `' . self::$m_bbPrefix . 'users` ORDER BY `user_id` DESC LIMIT 1');
        $ar = mysql_fetch_array($res);
        return $ar[0];
    }

    public static function updateUserData($userId, $arKvp)
    {
        $ret = -1;

        if( $userId > 0 )
        {

            $user = self::getUser($userId);

            if( $user != null )
            {
                $totalR = count($arKvp);
                $fields = '';
                foreach( $arKvp as $field => $value )
                {
                    $fields .= ', `' . $field . '` = "' . strip_tags($value) . '"';
                }

                $sql = sprintf(
                    'UPDATE `%susers` SET %s WHERE `user_id` = %d LIMIT 1'
                    , self::$m_bbPrefix
                    , substr($fields, 2)
                    , $userId
                );

                debugvar('Updating user #' . $userId . ': ' . $sql);
                $res = self::executeSql($sql);

                // User is updated
                $ret = $userId;

            }else
            {
                // No user exists
                $ret = 0;
            }
        }  // END IF ( no user ID )

        return $ret;
    }

    /**
    *   Returns user_id by username if such username exists
    *
    *   @param string $username username to check
    *   @return integer ID of user or 0 if does not exist
    */
    public static function usernameExists($username)
    {
        $res = self::executeSql('SELECT `user_id` FROM `' . self::$m_bbPrefix . 'users` WHERE `username` = "' . strip_tags($username) . '" LIMIT 1');
        if( ($myrow = mysql_fetch_array($res, MYSQL_ASSOC)) )
        {
            return $myrow['user_id'];
        }

        return 0;
    }

    /**
    *   Returns user from `phpbb_users` table by `user_id`
    *
    *   @param integer $userId primary key value
    *   @return PhpBbUser object or null
    */
    public static function getUser($userId)
    {
        $res = self::executeSql('SELECT * FROM `' . self::$m_bbPrefix . 'users` WHERE `user_id` = ' . (int)$userId . ' LIMIT 1');
        if( ($myrow = mysql_fetch_array($res, MYSQL_ASSOC)) )
        {
            return self::hydrateUser($myrow);
        }

        return null;
    }

    /**
    *   Returns array of latest post from `phpbb_posts`
    *
    *   @param integer $max limit of posts to retrieve
    *   @return array of PhpBbPost
    */
    public static function getLatestPosts($max = 10)
    {
        $res = self::executeSql('SELECT * FROM `' . self::$m_bbPrefix . 'posts` ORDER by `post_id` DESC LIMIT ' . $max);
        
        $arPosts = array();
        while( ($myrow = mysql_fetch_array($res, MYSQL_ASSOC)) )
        {
            $obj = new PhpBbPost();
            $obj->setPostId($myrow['post_id']);
            $obj->setTopicId($myrow['topic_id']);
            $obj->setForumId($myrow['forum_id']);
            $obj->setPosterId($myrow['poster_id']);
            $obj->setIconId($myrow['icon_id']);
            $obj->setPosterIp($myrow['poster_ip']);
            $obj->setPostTime($myrow['post_time']);
            $obj->setPostApproved($myrow['post_approved']);
            $obj->setPostReported($myrow['post_reported']);
            $obj->setEnableBbcode($myrow['enable_bbcode']);
            $obj->setEnableSmilies($myrow['enable_smilies']);
            $obj->setEnableMagicUrl($myrow['enable_magic_url']);
            $obj->setEnableSig($myrow['enable_sig']);
            $obj->setPostUsername($myrow['post_username']);
            $obj->setPostSubject($myrow['post_subject']);
            $obj->setPostText($myrow['post_text']);
            $obj->setPostChecksum($myrow['post_checksum']);
            $obj->setPostAttachment($myrow['post_attachment']);
            $obj->setBbcodeBitfield($myrow['bbcode_bitfield']);
            $obj->setBbcodeUid($myrow['bbcode_uid']);
            $obj->setPostPostcount($myrow['post_postcount']);
            $obj->setPostEditTime($myrow['post_edit_time']);
            $obj->setPostEditReason($myrow['post_edit_reason']);
            $obj->setPostEditUser($myrow['post_edit_user']);
            $obj->setPostEditCount($myrow['post_edit_count']);
            $obj->setPostEditLocked($myrow['post_edit_locked']);
            $arPosts[] = $obj;
        }

        return $arPosts;
    }

    /**
    *   Executes SQL and returns MySQL resource
    *
    *   @param string $sql SQL to execute
    *   @return resource MySQL resource
    */
    protected static function executeSql($sql)
    {
        if( !($res = @mysql_query($sql, self::getDbConnection())) )
        {
            throw new Exception('Error executing: ' . $sql . '!<br />' . mysql_error());
        }

        return $res;
    }

    /**
    *   Returns DB link from Propel
    *
    *   @return MySQL link
    */
    protected static function getDbConnection()
    {
        return Propel::getConnection()->getResource();
    }


    /**
    *   Returns PhpBbUser object out of array
    *
    *   @param array $myrow array from mysql_fetch_array()
    *   @return PhpBbUser or null
    */
    protected static function hydrateUser($myrow)
    {
        $obj = null;

        if( !empty($myrow) )
        {
            $obj = new PhpBbUser();
            $obj->setUserId($myrow['user_id']);
            $obj->setUserType($myrow['user_type']);
            $obj->setGroupId($myrow['group_id']);
            $obj->setUserPermissions($myrow['user_permissions']);
            $obj->setUserPermFrom($myrow['user_perm_from']);
            $obj->setUserIp($myrow['user_ip']);
            $obj->setUserRegdate($myrow['user_regdate']);
            $obj->setUsername($myrow['username']);
            $obj->setUsernameClean($myrow['username_clean']);
            $obj->setUserPassword($myrow['user_password']);
            $obj->setUserPasschg($myrow['user_passchg']);
            $obj->setUserPassConvert($myrow['user_pass_convert']);
            $obj->setUserEmail($myrow['user_email']);
            $obj->setUserEmailHash($myrow['user_email_hash']);
            $obj->setUserBirthday($myrow['user_birthday']);
            $obj->setUserLastvisit($myrow['user_lastvisit']);
            $obj->setUserLastmark($myrow['user_lastmark']);
            $obj->setUserLastpostTime($myrow['user_lastpost_time']);
            $obj->setUserLastpage($myrow['user_lastpage']);
            $obj->setUserLastConfirmKey($myrow['user_last_confirm_key']);
            $obj->setUserLastSearch($myrow['user_last_search']);
            $obj->setUserWarnings($myrow['user_warnings']);
            $obj->setUserLastWarning($myrow['user_last_warning']);
            $obj->setUserLoginAttempts($myrow['user_login_attempts']);
            $obj->setUserInactiveReason($myrow['user_inactive_reason']);
            $obj->setUserInactiveTime($myrow['user_inactive_time']);
            $obj->setUserPosts($myrow['user_posts']);
            $obj->setUserLang($myrow['user_lang']);
            $obj->setUserTimezone($myrow['user_timezone']);
            $obj->setUserDst($myrow['user_dst']);
            $obj->setUserDateformat($myrow['user_dateformat']);
            $obj->setUserStyle($myrow['user_style']);
            $obj->setUserRank($myrow['user_rank']);
            $obj->setUserColour($myrow['user_colour']);
            $obj->setUserNewPrivmsg($myrow['user_new_privmsg']);
            $obj->setUserUnreadPrivmsg($myrow['user_unread_privmsg']);
            $obj->setUserLastPrivmsg($myrow['user_last_privmsg']);
            $obj->setUserMessageRules($myrow['user_message_rules']);
            $obj->setUserFullFolder($myrow['user_full_folder']);
            $obj->setUserEmailtime($myrow['user_emailtime']);
            $obj->setUserTopicShowDays($myrow['user_topic_show_days']);
            $obj->setUserTopicSortbyType($myrow['user_topic_sortby_type']);
            $obj->setUserTopicSortbyDir($myrow['user_topic_sortby_dir']);
            $obj->setUserPostShowDays($myrow['user_post_show_days']);
            $obj->setUserPostSortbyType($myrow['user_post_sortby_type']);
            $obj->setUserPostSortbyDir($myrow['user_post_sortby_dir']);
            $obj->setUserNotify($myrow['user_notify']);
            $obj->setUserNotifyPm($myrow['user_notify_pm']);
            $obj->setUserNotifyType($myrow['user_notify_type']);
            $obj->setUserAllowPm($myrow['user_allow_pm']);
            $obj->setUserAllowViewonline($myrow['user_allow_viewonline']);
            $obj->setUserAllowViewemail($myrow['user_allow_viewemail']);
            $obj->setUserAllowMassemail($myrow['user_allow_massemail']);
            $obj->setUserOptions($myrow['user_options']);
            $obj->setUserAvatar($myrow['user_avatar']);
            $obj->setUserAvatarType($myrow['user_avatar_type']);
            $obj->setUserAvatarWidth($myrow['user_avatar_width']);
            $obj->setUserAvatarHeight($myrow['user_avatar_height']);
            $obj->setUserSig($myrow['user_sig']);
            $obj->setUserSigBbcodeUid($myrow['user_sig_bbcode_uid']);
            $obj->setUserSigBbcodeBitfield($myrow['user_sig_bbcode_bitfield']);
            $obj->setUserFrom($myrow['user_from']);
            $obj->setUserIcq($myrow['user_icq']);
            $obj->setUserAim($myrow['user_aim']);
            $obj->setUserYim($myrow['user_yim']);
            $obj->setUserMsnm($myrow['user_msnm']);
            $obj->setUserJabber($myrow['user_jabber']);
            $obj->setUserWebsite($myrow['user_website']);
            $obj->setUserOcc($myrow['user_occ']);
            $obj->setUserInterests($myrow['user_interests']);
            $obj->setUserActkey($myrow['user_actkey']);
            $obj->setUserNewpasswd($myrow['user_newpasswd']);
            $obj->setUserFormSalt($myrow['user_form_salt']);
            $obj->setUserNew($myrow['user_new']);
            $obj->setUserReminded($myrow['user_reminded']);
            $obj->setUserRemindedTime($myrow['user_reminded_time']);
        }

        return $obj;
    }

    protected static function phpBbHash($password)
    {
        $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

        $random_state = self::uniqueId();
        $random = '';
        $count = 6;

        if (($fh = @fopen('/dev/urandom', 'rb')))
        {
            $random = fread($fh, $count);
            fclose($fh);
        }

        if (strlen($random) < $count)
        {
            $random = '';

            for ($i = 0; $i < $count; $i += 16)
            {
                $random_state = md5(self::uniqueId() . $random_state);
                $random .= pack('H*', md5($random_state));
            }
            $random = substr($random, 0, $count);
        }
        debugvar('Create random for phpBB hash: ' . $random);

        $hash = self::hashCryptPrivate($password, self::hashGensaltPrivate($random, $itoa64), $itoa64);

        if (strlen($hash) == 34)
        {
            return $hash;
        }

        return md5($password);
    }

    /**
    * Returns unique ID
    * @param string $extra additional entropy
    */
    public static function uniqueId($extra = 'c')
    {
        $val = md5(microtime());
        return substr($val, 4, 16);
    }


     /**
    * Generate salt for hash generation
    */
    protected static function hashGensaltPrivate($input, &$itoa64, $iteration_count_log2 = 6)
    {
        if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
        {
            $iteration_count_log2 = 8;
        }

        $output = '$H$';
        $output .= $itoa64[min($iteration_count_log2 + ((PHP_VERSION >= 5) ? 5 : 3), 30)];
        $output .= self::hashEncode64($input, 6, $itoa64);

        return $output;
    }

    /**
    * Encode hash
    */
    protected static function hashEncode64($input, $count, &$itoa64)
    {
        $output = '';
        $i = 0;

        do
        {
            $value = ord($input[$i++]);
            $output .= $itoa64[$value & 0x3f];

            if ($i < $count)
            {
                $value |= ord($input[$i]) << 8;
            }

            $output .= $itoa64[($value >> 6) & 0x3f];

            if ($i++ >= $count)
            {
                break;
            }

            if ($i < $count)
            {
                $value |= ord($input[$i]) << 16;
            }

            $output .= $itoa64[($value >> 12) & 0x3f];

            if ($i++ >= $count)
            {
                break;
            }

            $output .= $itoa64[($value >> 18) & 0x3f];
        }
        while ($i < $count);

        return $output;
    }

    /**
    * The crypt function/replacement
    */
    protected static function hashCryptPrivate($password, $setting, &$itoa64)
    {
        $output = '*';

        // Check for correct hash
        if (substr($setting, 0, 3) != '$H$')
        {
            return $output;
        }

        $count_log2 = strpos($itoa64, $setting[3]);

        if ($count_log2 < 7 || $count_log2 > 30)
        {
            return $output;
        }

        $count = 1 << $count_log2;
        $salt = substr($setting, 4, 8);

        if (strlen($salt) != 8)
        {
            return $output;
        }

        /**
        * We're kind of forced to use MD5 here since it's the only
        * cryptographic primitive available in all versions of PHP
        * currently in use.  To implement our own low-level crypto
        * in PHP would result in much worse performance and
        * consequently in lower iteration counts and hashes that are
        * quicker to crack (by non-PHP code).
        */
        if (PHP_VERSION >= 5)
        {
            $hash = md5($salt . $password, true);
            do
            {
                $hash = md5($hash . $password, true);
            }
            while (--$count);
        }
        else
        {
            $hash = pack('H*', md5($salt . $password));
            do
            {
                $hash = pack('H*', md5($hash . $password));
            }
            while (--$count);
        }

        $output = substr($setting, 0, 12);
        $output .= self::hashEncode64($hash, 16, $itoa64);

        return $output;
    }



}